/** \file
* WIN32-specific implementation of the XML file source class.
* This file is part of Chorus, available at
* http://code.google.com/p/chorus-twitter/ . This file and all associated files
* and derivative works are bound by the terms of the LGPL 3.0 license,
* available at http://www.gnu.org/licenses/lgpl-3.0-standalone.html .
* \par Subversion URL:
* \$URL: http://chorus-twitter.googlecode.com/svn/branches/cpp/src/xml/win32/XmlFileSource.cpp $
* \version \$Rev: 153 $
* \date    \$Date: 2010-04-06 07:43:42 +0000 (Tue, 06 Apr 2010) $
*/

#define NEEDS_WIN32
#include "config.h"

#include <cstring>

#include <windows.h>

#include "system/DebugConsole.hpp"
#include "xml/XmlFileSource.hpp"

using namespace std;

XmlFileSource::XmlFileSource(XmlTree *treeInit, const char *pathInit) :
	XmlSource(treeInit, localizePath(pathInit))
{
}

XmlFileSource::~XmlFileSource()
{
}

std::string XmlFileSource::localizePath(const char *pathInit)
{
	const unsigned userpathlen = GetEnvironmentVariable("appdata", 0, 0);
	char *userpath = new char[userpathlen];
	if (!userpath)
	{
		PDEBUG("new failed");
		return "";
	}
	const unsigned nread = GetEnvironmentVariable("appdata", userpath,
		userpathlen);
	if (nread != userpathlen-1)
	{
		PDEBUG_WIN32("GetEnvironmentVariable failed");
		delete [] userpath;
		return "";
	}

	string syspath = userpath;
	syspath += "\\~chorus\\";
	syspath += pathInit;

	userpath[3] = 0;
	if (!GetDiskFreeSpace(userpath, 0, &sectorsize, 0, 0))
	{
		delete [] userpath;
		return "";
	}
	// Read 8 sectors worth of data, usually 4kB
	readsize = 8*sectorsize;

	delete [] userpath;

	return syspath;
}

bool XmlFileSource::perform()
{
	bool success = false;

	const HANDLE hfile = CreateFile(
		path.c_str(),
		GENERIC_READ,
		FILE_SHARE_READ,
		0,
		OPEN_EXISTING,
		FILE_ATTRIBUTE_NORMAL | FILE_FLAG_NO_BUFFERING | FILE_FLAG_OVERLAPPED |
		FILE_FLAG_WRITE_THROUGH | FILE_FLAG_SEQUENTIAL_SCAN,
		0);
	if (hfile == INVALID_HANDLE_VALUE)
	{
		PDEBUG_WIN32("CreateFile failed");
		return false;
	}

	char
		*buffer0 = (char*)_aligned_malloc(readsize, readsize),
		*buffer1 = (char*)_aligned_malloc(readsize, readsize);
	if (!(buffer0 && buffer1))
	{
		PDEBUG("_aligned_malloc failed");
		goto failure;
	}

	union
	{
		struct
		{
			ULONG_PTR Internal;
			ULONG_PTR InternalHigh;
			unsigned long long val64;
		} offs;
		OVERLAPPED ov;
	} ov;
	ov.ov.hEvent = 0;
	ov.ov.Internal = 0;
	ov.ov.InternalHigh = 0;
	ov.offs.val64 = 0;

	const unsigned long long &val64_check = ov.offs.val64;
	unsigned long long filesize = GetFileSize(hfile, 1+(DWORD*)&filesize);
	if ((filesize & 0xFFFFFFFF) == INVALID_FILE_SIZE)
	{
		const DWORD error = GetLastError();
		if (error != ERROR_SUCCESS)
		{
			PDEBUG_WIN32("GetFileSize failed");
			goto failure;
		}
	}

	DWORD nread = 0;
	do
	{
		// Perform the read request and return immediately whether or not it's
		// done
		const bool breadfile = ReadFile(hfile, buffer0, readsize, 0, &ov.ov);
		DWORD error = GetLastError();

		// bad read call
		if (!breadfile && error != ERROR_IO_PENDING)
		{
			PDEBUG_WIN32("ReadFile failed");
			goto failure;
		}

		// Skip the first iteration
		if (ov.offs.val64)
		{
			// Process the results of the previous read
			if (!writeData(buffer1, nread))
				goto failure;
		}

		// Block on this read request until completed
		if (!GetOverlappedResult(hfile, &ov.ov, &nread, TRUE))
		{
			// Bad GetOverlappedResult call
			PDEBUG_WIN32("GetOverlappedResult failed");
			goto failure;
		}

		// Swap buffers
		char *const tempbuff0 = buffer0;
		buffer0 = buffer1;
		buffer1 = tempbuff0;

		// Increment offset and check for EOF
		ov.offs.val64 += nread;
	} while (ov.offs.val64 < filesize);

	// Finish up the rest
	if (!writeData(buffer1, nread))
		goto failure;

	success = true;
failure:
	if (!CloseHandle(hfile))
		success = false;
	if (buffer0)
		_aligned_free(buffer0);
	if (buffer1)
		_aligned_free(buffer1);


	return success;
}
